package os.kernelProcess.schedulers;

import java.util.Enumeration;
import java.util.Hashtable;

import os.MiniMutex;
import os.MiniProcess;
import os.MiniThread;
import os.kernelProcess.SchedulerThread;
import os.kernelProcess.queues.ShortestQueue;

public class SPF extends SchedulerThread
{
	private MiniMutex threadRunning;
	//This is added as in using running thread...if it was null an exception occur
	private MiniMutex runningThreadChanging;
	
	public SPF(MiniProcess process, String tid) 
	{
		super(process, tid, 0);
		readyQueue = new ShortestQueue();
		suspendedQueue = new Hashtable<String, MiniThread>();
		threadRunning = new MiniMutex(0);
		runningThreadChanging = new MiniMutex(1);
		System.out.println("SPF");
	}

	/**
	 * Method that handles the scheduling of threads.
	 */
	// tested
	@Override
	public void schedule()
	{
		// XXX Done RoundRobinScheduler::schedule()
		while (true)
		{
			MiniThread head = null;
			while (readyQueue.getSize() == 0)
			{
				System.err.println(readyQueue.getSize());
			}
			System.err.println(readyQueue.getSize());
			this.runningThreadChanging.P();
				if(readyQueue.getSize() == 0)
				{
					this.runningThreadChanging.V();
					continue;
				}
				head = readyQueue.getHead();
//				sleep(500);
			
				runningThread = head;
			this.runningThreadChanging.V();
			System.err.println("Thread no "+head.getTid()+" is entering running state now "+readyQueue.getSize());
			//view.displayMessage("Thread no "+head.getTid()+" is entering running state now");
			readyQueue.removeThread(head.getTid());
			head.setThreadState(MiniThread.THREAD_RUNNING);
			head.resumeExecution();
			this.threadRunning.P(); /// TODO
			System.err.println("schedule interrupted before quantum "+readyQueue.getSize());
		}
	}
	
	@Override
	public void passThreadArguments(Object[] arguments) {
	}

	/**
	 * <b>UnImplemented</b>: Invoked to indicate that the <b>running thread</b>
	 * terminates its code. Note that it should be called when the thread
	 * terminates on its own.
	 */
	// XXX tested
	public void threadTerminated() {
		System.err.println("threadTerminated "+readyQueue.getSize());
		this.threadRunning.V();
	}

	/**
	 * Invoked when the user chooses to terminate the given process.
	 * 
	 * @param process
	 *            Process to be terminated.
	 */
	public void terminateProcess(MiniProcess process) {
		// XXX not tested SchedulerThread::terminateProcess
		Hashtable<String, MiniThread> threads = process.getThreads();
		Enumeration<MiniThread> e = threads.elements();
		while (e.hasMoreElements()) {
			terminateThread(e.nextElement());
		}
	}

	/**
	 * <b>UnImplemented</b>: Invoked when the user chooses to terminate either
	 * the given thread or the process to which this thread belongs. Note that
	 * in this case the thread is forced to terminate by the user.
	 * 
	 * @param thread
	 *            Thread to be terminated.
	 */
	public void terminateThread(MiniThread thread)
	{
		// XXX: not tested MiniScheduler::terminateThread()
		System.err.println("terminate Thread "+readyQueue.getSize());
		thread.getProcess().threadTerminated(thread.getTid());
		int threadState = thread.getThreadState();
		switch (threadState) 
		{
			case THREAD_SUSPENDED_READY:
			case THREAD_SUSPENDED_BLOCKED:
				suspendedQueue.remove(thread.getTid());
				break;
			case THREAD_RUNNING:
			case THREAD_BLOCKED:
//				this.runningThreadChanging.P();
//					runningThread = null;
//				this.runningThreadChanging.V();
				this.threadRunning.V();
				break;
			default:
				readyQueue.removeThread(thread.getTid());
				break;
		}
	}
	

	/**
	 * <b>UnImplemented</b>: Invoked when the thread requests and IO operation.
	 * The thread is blocked until the completion of the IO operation.
	 */
	// tested
	public void blockThread() 
	{
		// XXX Done MiniScheduler::blockThread()
		System.err.println("blockThread " + readyQueue.getSize());
		runningThread.setThreadState(THREAD_BLOCKED);
		runningThread.stopExecution();
	}

	/**
	 * <b>UnImplemented</b>: Invoked when an IO operation is completed for the
	 * given thread. The thread should be unblocked.
	 * 
	 * @param thread
	 *            Thread to unblock.
	 */
	// tested
	public void unblockThread(MiniThread thread)
	{
		// XXX: Done MiniScheduler::unblockThread()
		System.err.println("unblockThread "+readyQueue.getSize());
		int state = thread.getThreadState();
		switch (state)
		{
			case THREAD_BLOCKED:
				thread.setThreadState(THREAD_RUNNING);
				runningThread.resumeExecution();
				break;
			case THREAD_SUSPENDED_BLOCKED:
				thread.setThreadState(THREAD_SUSPENDED_READY);
				break;
		}
	}

	/**
	 * <b>UnImplemented</b>: Invoked when the user chooses to suspend either the
	 * given thread or the process to which this thread belongs.
	 * 
	 * @param thread
	 *            Thread to suspend.
	 */
	// tested
	public void suspendThread(MiniThread thread)
	{
		this.runningThreadChanging.P();
		// XXX Done MiniScheduler::suspendThread()
		thread.stopExecution();
		int state = thread.getThreadState();
		switch (state) 
		{
			case THREAD_RUNNING:
				thread.setThreadState(THREAD_SUSPENDED_READY);
				suspendedQueue.put(thread.getTid(), thread);
//				this.runningThreadChanging.P();
//					runningThread = null;
				this.runningThreadChanging.V();
				this.threadRunning.V();
				break;
			case THREAD_BLOCKED:
				thread.setThreadState(THREAD_SUSPENDED_BLOCKED);
				suspendedQueue.put(thread.getTid(), thread);
//				this.runningThreadChanging.P();
//					runningThread = null;
				this.runningThreadChanging.V();
				this.threadRunning.V();
				break;
			case THREAD_READY:
				readyQueue.removeThread(thread.getTid());
				thread.setThreadState(THREAD_SUSPENDED_READY);
				suspendedQueue.put(thread.getTid(), thread);
				this.runningThreadChanging.V();
				break;
		}
		
	}

	/**
	 * <b>UnImplemented</b>: Invoked when the user chooses to resume either the
	 * given thread or the process to which this thread belongs. Note that the
	 * thread should be suspended first in order to be resumed!
	 * 
	 * @param thread
	 *            Thread to resume.
	 */
	// tested
	public void resumeThread(MiniThread thread)
	{
		// XXX Done MiniScheduler::resumeThread()
		suspendedQueue.remove(thread.getTid());
		int state = thread.getThreadState();
		switch (state)
		{
			case THREAD_SUSPENDED_BLOCKED:
				thread.setThreadState(THREAD_BLOCKED);
				readyQueue.append(thread);
				break;
			case THREAD_SUSPENDED_READY:
				thread.setThreadState(THREAD_READY);
				readyQueue.append(thread);
				break;
		}
	}


	@Override
	public void threadStopped(MiniThread thread)
	{
		this.runningThreadChanging.P();
//		if(runningThread != null)
//		{
			System.err.println("thread stopped " + readyQueue.getSize());
			readyQueue.append(thread);
			thread.setThreadState(MiniThread.THREAD_READY);
//			runningThread = null;
			this.threadRunning.V();
//		}
		this.runningThreadChanging.V();
	}

	public String getSchedulerName()
	{
		return "SPF";
	}

	@Override
	public int getQuantum()
	{
		return quantum;
	}
}
